/*
 * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.godtree.mybatisplus.method;

import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.godtree.mybatisplus.enums.MySqlMethod;
import java.util.List;
import java.util.function.Predicate;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

/**
 * 批量新增数据,自选字段 insert
 * <p> 不同的数据库支持度不一样!!!  只在 mysql 下测试过!!!  只在 mysql 下测试过!!!  只在 mysql 下测试过!!! </p>
 * <p> 除了主键是 <strong> 数据库自增的未测试 </strong> 外理论上都可以使用!!! </p>
 * <p> 如果你使用自增有报错或主键值无法回写到entity,就不要跑来问为什么了,因为我也不知道!!! </p>
 * <p>
 * 自己的通用 mapper 如下使用:
 * <pre>
 * int insertBatchSomeColumn(List<T> entityList);
 * </pre>
 * </p>
 *
 * <li> 注意: 这是自选字段 insert !!,如果个别字段在 entity 里为 null 但是数据库中有配置默认值, insert 后数据库字段是为 null 而不是默认值 </li>
 *
 * <p>
 * 常用的 {@link Predicate}:
 * </p>
 *
 * <li> 例1: t -> !t.isLogicDelete() , 表示不要逻辑删除字段 </li>
 * <li> 例2: t -> !t.getProperty().equals("version") , 表示不要字段名为 version 的字段 </li>
 * <li> 例3: t -> t.getFieldFill() != FieldFill.UPDATE) , 表示不要填充策略为 UPDATE 的字段 </li>
 *
 * @author miemie
 * @since 2018-11-29
 */
@NoArgsConstructor
@AllArgsConstructor
public class ReplaceIntoBatchSomeColumn extends AbstractMethod {

  /**
   * 字段筛选条件
   */
  @Setter
  @Accessors(chain = true)
  private Predicate<TableFieldInfo> predicate;

  @SuppressWarnings("Duplicates")
  @Override
  public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass,
      TableInfo tableInfo) {
    KeyGenerator keyGenerator = new NoKeyGenerator();
    MySqlMethod sqlMethod = MySqlMethod.REPLACE_INTO_BATCH_SOME_COLUMN;
    List<TableFieldInfo> fieldList = tableInfo.getFieldList();
    String insertSqlColumn =
        tableInfo.getKeyInsertSqlColumn(true, false) + this.filterTableFieldInfo(fieldList,
            predicate,
            TableFieldInfo::getInsertSqlColumn, EMPTY);
    String columnScript =
        LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
    String insertSqlProperty =
        tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) + this.filterTableFieldInfo(
            fieldList,
            predicate,
            i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
    insertSqlProperty =
        LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1)
            + RIGHT_BRACKET;
    String valuesScript =
        SqlScriptUtils.convertForeach(insertSqlProperty, Constants.COLLECTION, null, ENTITY, COMMA);
    String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript,
        valuesScript);
    SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
    return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource,
        keyGenerator,
        null, null);
  }

  @Override
  public String getMethod(SqlMethod sqlMethod) {
    // 自定义 mapper 方法名
    return "replaceIntoBatchSomeColumn";
  }
}
